package com.xqboss.apps.service.equipment;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.xqboss.apps.constant.Config;
import com.xqboss.apps.constant.LockKey;
import com.xqboss.apps.domain.equipment.Equipment;
import com.xqboss.apps.domain.equipment.EquipmentAttrPointRatio;
import com.xqboss.apps.domain.equipment.EquipmentAttrTemplate;
import com.xqboss.apps.domain.member.SysUserNft;
import com.xqboss.apps.domain.nft.Nft;
import com.xqboss.apps.domain.nft.NftNumber;
import com.xqboss.apps.domain.punch.PunchUserRecord;
import com.xqboss.apps.domain.user.SysUserWallet;
import com.xqboss.apps.domain.user.UserAssetWrench;
import com.xqboss.apps.enums.nft.NftTypeEnum;
import com.xqboss.apps.enums.nft.UserNftStatusEnum;
import com.xqboss.apps.enums.user.UserWalletEnum;
import com.xqboss.apps.enums.user.WalletChangeDirectionEnum;
import com.xqboss.apps.enums.user.WalletChangeTypeEnum;
import com.xqboss.apps.enums.user.WalletWrenchChangeTypeEnum;
import com.xqboss.apps.mapper.equipment.EquipmentMapper;
import com.xqboss.apps.service.nft.NftService;
import com.xqboss.apps.service.punch.PunchUserConfigService;
import com.xqboss.apps.service.user.SysUserNftService;
import com.xqboss.apps.service.user.SysUserWalletService;
import com.xqboss.apps.service.user.UserAssetWrenchRecordService;
import com.xqboss.apps.service.user.UserAssetWrenchService;
import com.xqboss.apps.vo.equipment.RepairsDurabilityIntegralVo;
import com.xqboss.common.core.redis.lock.LockValue;
import com.xqboss.common.core.redis.lock.RedisLock;
import com.xqboss.common.exception.ServiceException;
import com.xqboss.system.service.ISysConfigService;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * @author Trent
 * @date 2023/8/16
 */
@Service
public class EquipmentService extends ServiceImpl<EquipmentMapper, Equipment> {

    @Autowired
    private EquipmentAttrTemplateService attrTemplateService;

    @Autowired
    private EquipmentAttrPointRatioService attrPointRatioService;

    @Autowired
    private SysUserNftService userNftService;

    @Autowired
    private SysUserWalletService userWalletService;

    @Autowired
    private UserAssetWrenchRecordService userAssetWrenchRecordService;

    @Autowired
    private UserAssetWrenchService userAssetWrenchService;

    @Autowired
    private ISysConfigService sysConfigService;

    @Autowired
    private PunchUserConfigService punchConfigService;

    @Autowired
    private EquipmentFixRecordService equipmentFixRecordService;

    @Autowired
    private NftService nftService;

    /**
     * 根据用户藏品ID获取装备信息
     *
     * @param userNftId
     * @param userId
     * @return
     */
    public Equipment getByUserNftId(Long userNftId, Long userId) {
        SysUserNft userNft = userNftService.getById(userNftId);
        if (Objects.isNull(userNft) || !Objects.equals(userNft.getUserId(), userId)) {
            throw new ServiceException("装备不存在！");
        }
        Equipment equipment = getByNftNumberId(userNft.getNftNumberId());
        return equipment;
    }

    /**
     * 根据藏品编号ID获取装备信息
     *
     * @param nftNumberId
     * @return
     */
    public Equipment getByNftNumberId(Long nftNumberId) {
        return getOne(new LambdaQueryWrapper<Equipment>()
                .eq(Equipment::getNftNumberId, nftNumberId));
    }

    /**
     * 装备打卡消耗
     *
     * @param record 打卡记录
     */
    @RedisLock(lockKey = LockKey.LOCK_EQUIPMENT_ATTR)
    @Transactional(rollbackFor = Exception.class)
    public void deductRecord(PunchUserRecord record) {
        Equipment equipment = getByNftNumberId(record.getNftNumberId());
        if (equipment.getBreakage().doubleValue() < record.getBreakageUse().doubleValue()) {
            throw new ServiceException("装备损坏，换个装备吧~");
        }
        if (equipment.getDurability().doubleValue() < record.getDurabilityUse().doubleValue()) {
            throw new ServiceException("耐久度不足，快去修复吧~");
        }
        boolean update = update(new LambdaUpdateWrapper<Equipment>()
                .set(Equipment::getBreakage, equipment.getBreakage().subtract(record.getBreakageUse()))
                .set(Equipment::getDurability, equipment.getDurability().subtract(record.getDurabilityUse()))
                .eq(Equipment::getId, equipment.getId()));
        if (!update) {
            throw new ServiceException("系统繁忙");
        }
    }

    /**
     * 初始化装备属性
     *
     * @param nft
     * @param nftNumber
     * @param isOpen
     */
    @Transactional(rollbackFor = Exception.class)
    public void initEquipmentInfo(Nft nft, NftNumber nftNumber, boolean isOpen) {
        Equipment equipment = new Equipment();
        equipment.setEquipmentAttrTemplateId(nft.getAttrTemplateId());
        equipment.setIncomeRatio(nft.getCoefficient());
//        equipment.setMaintainRatio(nft.getMaintainRatio());

        equipment.setNftId(nft.getId());
        equipment.setNftNumberId(nftNumber.getId());
        equipment.setRarity(nft.getRarity());
        equipment.setLevel(1);
        equipment.setIsOpen(isOpen);
        if (isOpen) {
            openEquipment(nft, equipment);
        }
        boolean save = save(equipment);
        nftNumber.setEquipment(equipment);
        if (!save) {
            throw new ServiceException("操作失败！");
        }
    }

    /**
     * 修复装备耐久度
     */
    @Transactional(rollbackFor = Exception.class)
    @RedisLock(lockKey = LockKey.LOCK_FIX_EQUIPMENT)
    public void fixDurability(Long userId, @LockValue Long userNftId, BigDecimal integral,  BigDecimal wrench, BigDecimal targetDurability) {
        SysUserNft userNft = userNftService.getById(userNftId);
        if (Objects.isNull(userNft)
                || !Objects.equals(userNft.getUserId(), userId)         // 持有人不是自己
                || userNft.getNftType() != NftTypeEnum.EQUIPMENT        // 不是装备
                ) {
            throw new ServiceException("装备不存在！");
        }
        // 使用中和装备中才能修复
        if(userNft.getNftStatus() != UserNftStatusEnum.USED && userNft.getNftStatus() != UserNftStatusEnum.NORMAL){
            throw new ServiceException("装备状态不支持修复！");
        }
        if (userNft.getNftStatus() == UserNftStatusEnum.USED) {
            // 如果当前藏品正在装备中，判断是否在打卡时段内,如果在打卡时段内，则不可修复
            boolean isPunchTime = punchConfigService.isPunchTime(userId);
            if (isPunchTime) {
                //装备使用中，并且处于打卡时段
                throw new ServiceException("打卡中不能修复装备~");
            }
        }
        Equipment equipment = getByNftNumberId(userNft.getNftNumberId());
        if (targetDurability == null) {
            targetDurability = equipment.getTotalDurability();
        }
        if(equipment.getDurability().compareTo(equipment.getTotalDurability()) == 0){
            throw new ServiceException("耐久度充足不能修复~");
        }
        if (targetDurability.compareTo(equipment.getTotalDurability()) > 0) {
            throw new ServiceException("不能超出最大耐久度~");
        }
        if (targetDurability.compareTo(equipment.getDurability()) < 0) {
            throw new ServiceException("不能低于当前耐久度~");
        }
        //获取nft耐久修复系数
        Nft nft = nftService.getNft(equipment.getNftId());
        //如果nft没有配置消耗扳手，则使用系统公共消耗扳手配置
        if (nft != null && nft.getRepairsLevelWrench() == null) {
            nft.setRepairsLevelWrench(sysConfigService.getValue(Config.Sys.Punch.维修消耗等级_扳手));
            nft.setMaintainRatioWrench(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数_扳手));
        }
        //修复装备耐久需要消耗的热豆
        BigDecimal useIntegral = getFixDurabilityIntegral(equipment, targetDurability, nft == null ? null : nft.getMaintainRatio());
        if(useIntegral.subtract(integral).abs().compareTo(new BigDecimal("0.015")) > 0){
            // 积分对比时，针对最佳修复方案的计算方式，可能有计算误差问题， 误差<= 0.01都算正常
            throw new ServiceException("请重新计算花费热豆！");
        }
        //计算耐久修复消耗的扳手
        if (nft != null && nft.getRepairsLevelWrench() != null && equipment.getLevel().compareTo(nft.getRepairsLevelWrench()) >= 0) {
            BigDecimal useWrench = getFixDurabilityWrench(equipment, targetDurability, nft.getMaintainRatioWrench(), nft.getIsNewRepair());
            if (useWrench.subtract(wrench).abs().compareTo(new BigDecimal("0.015")) > 0) {
                // 扳手对比时，针对最佳修复方案的计算方式，可能有计算误差问题， 误差<= 0.01都算正常
                throw new ServiceException("请重新计算花费扳手！");
            }
            //扣除用户扳手
            userAssetWrenchRecordService.updateWrenchBalance(userId, WalletWrenchChangeTypeEnum.REPAIRS, WalletChangeDirectionEnum.OUT, wrench, equipment.getId(), "维修装备消耗扳手");
        }
        userWalletService.reduce(userId, UserWalletEnum.INTEGRAL, integral, WalletChangeTypeEnum.REPAIRS, equipment.getId(), "维修装备消耗");
        update(new LambdaUpdateWrapper<Equipment>()
                .set(Equipment::getDurability, targetDurability)
                .eq(Equipment::getId, equipment.getId()));
        // 添加修复记录
        equipmentFixRecordService.add(userId, userNftId, userNft.getNftNumberId(), equipment.getDurability(), targetDurability, integral, wrench);
    }

    /**
     * 计算修复装备耐久度需要消耗的积分
     */
    public BigDecimal getFixDurabilityIntegral(Equipment equipment, BigDecimal targetDurability, BigDecimal maintainRatio) {
        //维修消耗 = 幸运值 × 维修维修系数 * 需要修复的耐久值
        // 需要修复的耐久度
        BigDecimal fixDurability = targetDurability.subtract(equipment.getDurability());
        if (fixDurability.compareTo(BigDecimal.ZERO) <= 0) {
            return BigDecimal.ZERO;
        }
        // 如果 ratio 为 0，直接返回0
        if (maintainRatio != null && maintainRatio.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        // 保留小数位数方式为舍入
        return fixDurability.multiply(BigDecimal.valueOf(equipment.getLuck()))
                .multiply(
                        Optional.ofNullable(maintainRatio)
                                .orElse(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数))
                )
                .setScale(2, RoundingMode.DOWN)
                .max(new BigDecimal("0.01"));
//        // 维修费用 = （幸运*维修系数）/（总属性点数*耐久消耗系数）*耐久维修点数
//        return BigDecimal.valueOf(equipment.getLuck())
//                .multiply(
//                        Optional.ofNullable(maintainRatio)
//                                .orElse(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数))
//                )
//                .divide(BigDecimal.valueOf(equipment.getTotalAttrCount())
//                        .multiply(sysConfigService.getValue(Config.Sys.Punch.耐久消耗系数)), 5, RoundingMode.HALF_UP
//                )
//                .multiply(fixDurability)
//                .setScale(2, RoundingMode.DOWN)
//                .max(new BigDecimal("0.01"));
    }

    /**
     * 计算修复装备耐久度需要消耗的扳手
     */
    public BigDecimal getFixDurabilityWrench(Equipment equipment, BigDecimal targetDurability, BigDecimal ratio, Integer isNew) {
        //维修消耗扳手 = 幸运值 × 维修消耗系数（扳手） * 需要修复的耐久值
        // 需要修复的耐久度
        BigDecimal fixDurability = targetDurability.subtract(equipment.getDurability());
        if (fixDurability.compareTo(BigDecimal.ZERO) <= 0) {
            return BigDecimal.ZERO;
        }
        // 如果 ratio 为 0，直接返回0
        if (ratio != null && ratio.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        if (isNew == 1) {
            // 维修费用 = （幸运*维修系数）/（总属性点数*耐久消耗系数）*耐久维修点数
            return BigDecimal.valueOf(equipment.getLuck())
                    .multiply(
                            Optional.ofNullable(ratio)
                                    .orElse(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数_扳手))
                    )
                    .divide(BigDecimal.valueOf(equipment.getTotalAttrCount())
                            .multiply(sysConfigService.getValue(Config.Sys.Punch.耐久消耗系数)), 5, RoundingMode.HALF_UP
                    )
                    .multiply(fixDurability)
                    .setScale(2, RoundingMode.DOWN)
                    .max(new BigDecimal("0.01"));
        } else {
            // 保留小数位数方式为舍入
            return fixDurability.multiply(BigDecimal.valueOf(equipment.getLuck()))
                    .multiply(
                            Optional.ofNullable(ratio)
                                    .orElse(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数_扳手))
                    )
                    .setScale(2, RoundingMode.DOWN)
                    .max(new BigDecimal("0.01"));
        }
    }

    /**
     * 计算指定积分可以修复装备耐久度数据
     */
    public BigDecimal getIntegralFixDurability(Equipment equipment, BigDecimal integral, BigDecimal maintainRatio) {
        //需要修复的耐久值 = 消耗积分 /（幸运值 × 维修维修系数）
        if(integral.compareTo(BigDecimal.ZERO) == 0){
            return BigDecimal.ZERO;
        }
        // 保留小数位数方式为舍入
        return integral.divide(
                BigDecimal.valueOf(equipment.getLuck())
                        .multiply(
                                Optional.ofNullable(maintainRatio)
                                        .orElse(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数))
                        )
                , 2, RoundingMode.DOWN);
    }

    /**
     * 计算指定扳手可以修复装备耐久度数据
     */
    public BigDecimal getWrenchFixDurability(Equipment equipment, BigDecimal wrench, BigDecimal ratio, Integer isNew) {
        //需要修复的耐久值 = 消耗扳手 /（幸运值 × 维修维修系数）
        if (wrench.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        if (isNew == 1) {
            // 剩余扳手/{（幸运*维修系数）/（总属性点数*耐久消耗系数）}
            return wrench.divide(
                    BigDecimal.valueOf(equipment.getLuck())
                            .multiply(
                                    Optional.ofNullable(ratio)
                                            .orElse(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数_扳手))
                            )
                            .divide(BigDecimal.valueOf(equipment.getTotalAttrCount())
                                    .multiply(sysConfigService.getValue(Config.Sys.Punch.耐久消耗系数)), 5, RoundingMode.HALF_UP
                            )
                    , 2, RoundingMode.DOWN);
        } else {
            // 保留小数位数方式为舍入
            return wrench.divide(
                    BigDecimal.valueOf(equipment.getLuck())
                            .multiply(
                                    Optional.ofNullable(ratio)
                                            .orElse(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数_扳手))
                            )
                    , 2, RoundingMode.DOWN);
        }
    }

    /**
     * 修复装备耐久度
     *
     * @return
     */
    public RepairsDurabilityIntegralVo calcFixDurability(Long userId, Long userNftId, BigDecimal targetDurability) {
        //维修消耗 = 幸运值 × 维修消耗系数
        Equipment equipment = getByUserNftId(userNftId, userId);
        if (targetDurability == null) {
            targetDurability = equipment.getTotalDurability();
        }
        if (targetDurability.compareTo(equipment.getTotalDurability()) > 0) {
            throw new ServiceException("不能超出最大耐久度~");
        }
        if (targetDurability.compareTo(equipment.getDurability()) < 0) {
            throw new ServiceException("不能低于当前耐久度~");
        }
        RepairsDurabilityIntegralVo result = new RepairsDurabilityIntegralVo();
        //获取nft耐久修复系数
        Nft nft = nftService.getNft(equipment.getNftId());
        //如果nft没有配置消耗扳手，则使用系统公共消耗扳手配置
        if (nft != null && nft.getRepairsLevelWrench() == null) {
            nft.setRepairsLevelWrench(sysConfigService.getValue(Config.Sys.Punch.维修消耗等级_扳手));
            nft.setMaintainRatioWrench(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数_扳手));
        }
        if (nft != null && nft.getMaintainRatio() == null) {
            nft.setMaintainRatio(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数));
        }
        if (targetDurability.compareTo(equipment.getDurability()) == 0) {
            //修复的目标值等于当前目标值，则不扣除
            result.setDurability(BigDecimal.ZERO);
            result.setDurabilityAfter(targetDurability);
            result.setIntegral(BigDecimal.ZERO);
        } else {
            // 修复到目标值需要消耗的积分值
            BigDecimal useInteger = getFixDurabilityIntegral(equipment, targetDurability, nft == null ? null : nft.getMaintainRatio());
            result.setDurability(targetDurability.subtract(equipment.getDurability()));
            result.setDurabilityAfter(targetDurability);
            result.setIntegral(useInteger);
        }
        SysUserWallet wallet = userWalletService.getWallet(userId, UserWalletEnum.INTEGRAL);
        result.setIntegralAfter(wallet.getAmount().subtract(result.getIntegral()));
        //装备修复耐久消耗扳手（X级及以上装备，修复装备同时消耗热豆和扳手，等级对应后台配置消耗扳手的等级）
        if ((nft != null && nft.getRepairsLevelWrench() != null && equipment.getLevel().compareTo(nft.getRepairsLevelWrench()) >= 0)) {
            if (targetDurability.compareTo(equipment.getDurability()) == 0) {
                result.setWrench(BigDecimal.ZERO);
            }else {
                BigDecimal useWrench = getFixDurabilityWrench(equipment, targetDurability, nft.getMaintainRatioWrench(), nft.getIsNewRepair());
                result.setWrench(useWrench);
            }
            UserAssetWrench userWrench = userAssetWrenchService.getWrench(userId);
            result.setWrenchAfter(userWrench.getAmount().subtract(result.getWrench()));
        }
        return result;
    }

    /**
     * 打开装备，分配装备属性
     *
     * @param nft
     * @param equipment
     */
    private void openEquipment(Nft nft, Equipment equipment) {
        // 获取装备属性模板
        EquipmentAttrTemplate attrTemplate = attrTemplateService.getById(nft.getAttrTemplateId());
        List<EquipmentAttrPointRatio> pointRatioList = attrPointRatioService.getByTemplateId(nft.getAttrTemplateId());
        // 根据爆率计算出基础点数量
        double randomRatio = new Random().nextDouble();
        EquipmentAttrPointRatio currentRatio = pointRatioList.get(0);
        for (EquipmentAttrPointRatio ratio : pointRatioList) {
            if (randomRatio <= ratio.getRatio().doubleValue()) {
                currentRatio = ratio;
                break;
            } else {
                randomRatio = randomRatio - ratio.getRatio().doubleValue();
            }
        }
        // 对爆率再根据配置的比例进行分配
        int luck = 1, talent = 1, durable = 1, strong = 1;
        Integer basePoint = currentRatio.getPoint() - 4;
        // 先分配4点基础属性值
        int pointAmount = basePoint;
        // 分配顺序： 幸运 、 坚固 、 耐久、 天赋， 不要求精确计算
        int luckValue = (int) Math.round(RandomUtils.nextDouble(attrTemplate.getLuckRatioMin().doubleValue() * basePoint, attrTemplate.getLuckRatioMax().doubleValue() * basePoint));
        if (luckValue <= pointAmount) {
            luck += luckValue;
            pointAmount -= luckValue;
        } else {
            luck += pointAmount;
            pointAmount = 0;
        }
        int strongValue = (int) Math.round(RandomUtils.nextDouble(attrTemplate.getStrongRatioMin().doubleValue() * basePoint, attrTemplate.getStrongRatioMax().doubleValue() * basePoint));
        if (strongValue <= pointAmount) {
            strong += strongValue;
            pointAmount -= strongValue;
        } else {
            strong += pointAmount;
            pointAmount = 0;
        }
        int durableValue = (int) Math.round(RandomUtils.nextDouble(attrTemplate.getDurableRatioMin().doubleValue() * basePoint, attrTemplate.getDurableRatioMax().doubleValue() * basePoint));
        if (durableValue <= pointAmount) {
            durable += durableValue;
            pointAmount -= durableValue;
        } else {
            durable += pointAmount;
            pointAmount = 0;
        }
        // 天赋不用计算，剩下的就全部分配给天赋
        //int talentValue = (int)(attrTemplate.getTalentRatioMin().doubleValue() * point + (attrTemplate.getTalentRatioMax().doubleValue() - attrTemplate.getTalentRatioMin().doubleValue()) * point);
        talent += pointAmount;

        // 基础属性分配完成
        // 计算破损度和耐久度
        // 破损度 = 坚固值 * 破损倍率
        BigDecimal breakage = BigDecimal.valueOf(strong).multiply(sysConfigService.getValue(Config.Sys.Punch.破损度倍数)).setScale(2, RoundingMode.HALF_UP);
        // 耐久度 = 耐久值 * 耐久倍数
        BigDecimal durability = BigDecimal.valueOf(durable).multiply(sysConfigService.getValue(Config.Sys.Punch.耐久度倍数)).setScale(2, RoundingMode.HALF_UP);

        equipment.setDurability(durability);
        equipment.setTotalDurability(equipment.getDurability());
        equipment.setBreakage(breakage);
        equipment.setTotalBreakage(equipment.getBreakage());

        equipment.setTotalAttrCount(currentRatio.getPoint());
        equipment.setLuck(luck);
        equipment.setTalent(talent);
        equipment.setDurable(durable);
        equipment.setStrong(strong);
    }

    public static void main(String[] args) {
        double ratio[] = {0.13, 0.19, 0.58, 0.09, 0.01};

        int count[] = {0, 0, 0, 0, 0};

        Random random = new Random();
        out:
        for (int i = 0; i < 100000; i++) {
            double value = random.nextDouble();
            for (int j = 0; j < ratio.length; j++) {
                if (value <= ratio[j]) {
                    count[j]++;
                    System.out.println(value + "    >>>>>   " + ratio[j]);
                    continue out;
                } else {
                    value = value - ratio[j];
                }
            }
        }

        int sum = 0;
        for (int i = 0; i < count.length; i++) {
            System.out.println(i + 1 + "   ---  " + count[i]);
            sum += count[i];
        }
        System.out.println("sum:" + sum);
    }

    /**
     * 获取修复耐久度最佳值
     *
     * @param userNftId
     * @param userId
     * @return
     */
    public RepairsDurabilityIntegralVo getBestFixDurability(Long userNftId, Long userId) {
        RepairsDurabilityIntegralVo result = new RepairsDurabilityIntegralVo();
        Equipment equipment = getByUserNftId(userNftId, userId);
        Nft nft = nftService.getNft(equipment.getNftId());
        //如果nft没有配置消耗扳手，则使用系统公共消耗扳手配置
        if (nft != null && nft.getRepairsLevelWrench() == null) {
            nft.setRepairsLevelWrench(sysConfigService.getValue(Config.Sys.Punch.维修消耗等级_扳手));
            nft.setMaintainRatioWrench(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数_扳手));
        }
        if (nft != null && nft.getMaintainRatio() == null) {
            nft.setMaintainRatio(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数));
        }
        BigDecimal durability = equipment.getDurability();
        BigDecimal totalDurability = equipment.getTotalDurability();
        BigDecimal waitFixDurability = totalDurability.subtract(durability);

        SysUserWallet wallet = userWalletService.getWallet(userId, UserWalletEnum.INTEGRAL);

        if (wallet.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
            // 没有积分信息，则无法修复
            result.setDurability(BigDecimal.ZERO);
            result.setIntegral(BigDecimal.ZERO);
            result.setIntegralAfter(wallet.getAmount());
            result.setDurabilityAfter(durability);
        } else if (waitFixDurability.compareTo(BigDecimal.ZERO) < 0) {
            // 如果需要修复的<= 0 ,则无需修复
            result.setDurability(BigDecimal.ZERO);
            result.setIntegral(BigDecimal.ZERO);
            result.setIntegralAfter(wallet.getAmount());
            result.setDurabilityAfter(durability);
        } else {
            // 计算修复该耐久度需要消耗多少积分
            BigDecimal needUseIntegral = getFixDurabilityIntegral(equipment, equipment.getTotalDurability(), nft == null ? null : nft.getMaintainRatio());
            if (wallet.getAmount().compareTo(needUseIntegral) < 0) {
                // 用户积分不足，以用户积分可以修复的值为准
                waitFixDurability = getIntegralFixDurability(equipment, wallet.getAmount(), nft == null ? null : nft.getMaintainRatio());
                result.setIntegral(wallet.getAmount());
                result.setIntegralAfter(BigDecimal.ZERO);
                result.setDurability(waitFixDurability);
                result.setDurabilityAfter(equipment.getDurability().add(waitFixDurability));
            } else {
                // 积分充足，则全额充值
                result.setIntegral(needUseIntegral);
                result.setIntegralAfter(wallet.getAmount().subtract(needUseIntegral));
                result.setDurability(waitFixDurability);
                result.setDurabilityAfter(totalDurability);
            }
        }
        //装备修复耐久消耗扳手（X级及以上装备，修复装备同时消耗热豆和扳手，等级对应后台配置消耗扳手的等级）
        if (nft != null && nft.getRepairsLevelWrench() != null && equipment.getLevel().compareTo(nft.getRepairsLevelWrench()) >= 0) {
            //计算耐久修复消耗的扳手
            result = getExpDurabilityWrench(equipment, nft, userId, result, nft.getMaintainRatioWrench());
        }

        return result;
    }

    /**
     * 计算修复耐久需要消耗的扳手
     */
    public RepairsDurabilityIntegralVo getExpDurabilityWrench(Equipment equipment, Nft nft, Long userId, RepairsDurabilityIntegralVo result, BigDecimal wrenchRatio) {
        //热豆修复的 耐久度
        BigDecimal durability = result.getDurability();

        UserAssetWrench wrench = userAssetWrenchService.getWrench(userId);

        // 计算修复该耐久度需要消耗多少扳手
        BigDecimal needUseWrench = getFixDurabilityWrench(equipment, equipment.getTotalDurability(), wrenchRatio, nft.getIsNewRepair());
        if (wrench.getAmount().compareTo(needUseWrench) < 0) {
            // 用户扳手不足，以用户扳手可以修复的值为准
            BigDecimal wrenchFixDurability = getWrenchFixDurability(equipment, wrench.getAmount(), wrenchRatio, nft.getIsNewRepair());
            result.setWrench(wrench.getAmount());
            result.setWrenchAfter(BigDecimal.ZERO);
            //比如热豆能修复10点，扳手能修复3点，把他最多就只能修复三点 取最低
            if (durability.compareTo(wrenchFixDurability) > 0) {
                result.setDurability(wrenchFixDurability);
                result.setDurabilityAfter(equipment.getDurability().add(wrenchFixDurability));
                //因为取了消耗最小值  重新计算热豆
                result.setIntegral(wrenchFixDurability.multiply(new BigDecimal(equipment.getLuck())).multiply(
                        Optional.ofNullable(nft.getMaintainRatio())
                                .orElse(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数))
                ).setScale(2, RoundingMode.DOWN));

            } else {
                if (nft.getIsNewRepair() == 0) {
                    result.setWrench(result.getDurability().multiply(new BigDecimal(equipment.getLuck())).multiply(
                            Optional.ofNullable(nft.getMaintainRatioWrench())
                                    .orElse(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数_扳手))
                    ).setScale(2, RoundingMode.DOWN));
                } else {
                    // 维修费用 = （幸运*维修系数）/（总属性点数*耐久消耗系数）*耐久维修点数
                    BigDecimal scale = BigDecimal.valueOf(equipment.getLuck())
                            .multiply(
                                    Optional.ofNullable(nft.getMaintainRatioWrench())
                                            .orElse(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数_扳手))
                            )
                            .divide(BigDecimal.valueOf(equipment.getTotalAttrCount())
                                    .multiply(sysConfigService.getValue(Config.Sys.Punch.耐久消耗系数)), 5, RoundingMode.HALF_UP
                            )
                            .multiply(result.getDurability())
                            .setScale(2, RoundingMode.DOWN);
                    result.setWrench(scale);
                }
            }
        } else {
            if (nft.getIsNewRepair() == 0) {
                // 扳手充足，则 修复耐久花费的扳手 = 耐久 * 幸运 * 扳手系数
                result.setWrench(result.getDurability().multiply(new BigDecimal(equipment.getLuck())).multiply(
                        Optional.ofNullable(nft.getMaintainRatioWrench())
                                .orElse(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数_扳手))
                ).setScale(2, RoundingMode.DOWN));
            } else {
                // 维修费用 = （幸运*维修系数）/（总属性点数*耐久消耗系数）*耐久维修点数
                result.setWrench(BigDecimal.valueOf(equipment.getLuck())
                        .multiply(
                                Optional.ofNullable(nft.getMaintainRatioWrench())
                                        .orElse(sysConfigService.getValue(Config.Sys.Punch.耐久维修系数_扳手))
                        )
                        .divide(BigDecimal.valueOf(equipment.getTotalAttrCount())
                                .multiply(sysConfigService.getValue(Config.Sys.Punch.耐久消耗系数)), 5, RoundingMode.HALF_UP
                        )
                        .multiply(result.getDurability())
                        .setScale(2, RoundingMode.DOWN));
                result.setWrenchAfter(wrench.getAmount().subtract(result.getWrench()));
            }
        }
        return result;
    }

    /**
     * 更新装备配置
     *
     * @param durabilityRate 耐久度倍率
     * @param breakageRate   破损度倍率
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateEquipmentConfig(BigDecimal durabilityRate, BigDecimal breakageRate) {
        if (Objects.isNull(durabilityRate) && Objects.isNull(breakageRate)) {
            return;
        }
        // 重新计算耐久度、破损度倍率
        int page = 1, pageSize = 1000;
        while (true) {
            Page<Object> startPage = PageHelper.startPage(page, pageSize);
            List<Equipment> equipment = list(new LambdaQueryWrapper<Equipment>()
                    .select(Equipment::getId, Equipment::getTotalDurability, Equipment::getDurability,
                            Equipment::getTotalBreakage, Equipment::getBreakage,
                            Equipment::getIsOpen,
                            Equipment::getDurable, Equipment::getStrong));

            List<Equipment> updateEquipments = new ArrayList<>();
            for (Equipment e : equipment) {
                if (BooleanUtils.isTrue(e.getIsOpen())) {
                    Equipment update = new Equipment();
                    update.setId(e.getId());
                    if (Objects.nonNull(durabilityRate)) {
                        // 重新计算耐久度
                        // 耐久度 = 耐久值 * 耐久倍数
                        BigDecimal durabilityTotal = BigDecimal.valueOf(e.getDurable()).multiply(durabilityRate).setScale(2, RoundingMode.HALF_UP);
                        // 计算已使用的耐久度
                        BigDecimal durabilityUse = e.getTotalDurability().subtract(e.getDurability());
                        BigDecimal newDurability = durabilityTotal.subtract(durabilityUse);
                        if (newDurability.compareTo(BigDecimal.ZERO) < 0) {
                            newDurability = BigDecimal.ZERO;
                        }
                        update.setTotalDurability(durabilityTotal);
                        update.setDurability(newDurability);
                    }
                    if (Objects.nonNull(breakageRate)) {
                        // 重新计算破损值
                        // 破损度 = 坚固值 * 破损倍率
                        BigDecimal breakageTotal = BigDecimal.valueOf(e.getStrong()).multiply(breakageRate).setScale(2, RoundingMode.HALF_UP);
                        // 计算已使用的耐久度
                        BigDecimal breakageUse = e.getTotalBreakage().subtract(e.getBreakage());
                        BigDecimal newBreakage = breakageTotal.subtract(breakageUse);
                        if (newBreakage.compareTo(BigDecimal.ZERO) < 0) {
                            newBreakage = BigDecimal.ZERO;
                        }
                        update.setTotalBreakage(breakageTotal);
                        update.setBreakage(newBreakage);

                    }
                    updateEquipments.add(update);
                }
            }
            updateBatchById(updateEquipments);
            startPage.close();
            if (equipment.size() < pageSize) {
                break;
            }
            page++;
        }
    }

    /**
     * 更新装备属性
     * @param newEquipment
     */
    public void updateAttribute(Equipment newEquipment) {
        boolean update = updateById(newEquipment);
        if(!update){
            throw new ServiceException("操作失败！");
        }
    }

    /**
     * 根据藏品编号ID给装备增加收入及经验值
     * @param nftNumberId
     * @param income
     */
    public boolean addIncomeExpByNftNumberId(Long nftNumberId, BigDecimal income, BigDecimal breakageUse) {
        return update(new LambdaUpdateWrapper<Equipment>()
                .setSql("total_income = total_income + " + income)
                .setSql("level_income = level_income + " + income)
                .setSql("current_exp = current_exp + " + breakageUse)
                .eq(Equipment::getNftNumberId, nftNumberId));
    }

    /**
     * 检查装备是否新装备
     * 1、根据装备id查询出装备判空
     * 2、判断装备是否新装备
     */
    public boolean checkEquipmentIsNew(Long equipmentId) {
        Equipment equipment = getById(equipmentId);
        if (Objects.isNull(equipment)) {
            throw new ServiceException("装备不存在！");
        }
        //全新装备指【满破损】且【满耐久】的装备
        return equipment.getBreakage().compareTo(equipment.getTotalBreakage()) == 0
                && equipment.getDurability().compareTo(equipment.getTotalDurability()) == 0;
    }
}
